#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>

#include "ThreadPool.hpp"
#include "InetAddr.hpp"
#include "log.hpp"
using namespace log_ns;

enum
{
    SOCKERROR = 1,
    BINDERROR,
    LISTENERROR

};

const static int gport = 8888;
const static int gbacklog = 8;
const static int SIZE = 1024;

using task_t = std::function<void()>;
using handler_t = std::function<std::string(std::string &)>;

class Tcpserver
{
public:
    Tcpserver(handler_t handler, int port = gport)
        : _handler(handler), _port(port)
    {
    }

    void Init()
    {
        _listen_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sockfd < 0)
        {
            LOG(FATAL, "socket error!\n");
            exit(SOCKERROR);
        }
        LOG(INFO, "socket success!\n");

        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_addr.s_addr = INADDR_ANY;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(_port);

        if (::bind(_listen_sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
        {
            LOG(FATAL, "bind error!\n");
            exit(BINDERROR);
        }
        LOG(INFO, "bind success!\n");

        if (::listen(_listen_sockfd, gbacklog) == -1)
        {
            LOG(FATAL, "listen error!\n");
            exit(LISTENERROR);
        }
        LOG(INFO, "listen success!\n");
    }

    void Start()
    {
        while (true)
        {
            struct sockaddr_in client_addr;
            socklen_t len = sizeof(client_addr);
            int sockfd = ::accept(_listen_sockfd, (struct sockaddr *)&client_addr, &len);
            if (sockfd < 0)
            {
                LOG(FATAL, "accept error!\n");
                continue;
            }
            InetAddr C_addr(client_addr);
            // 处理上层任务
            ThreadPool<task_t>::GetInstance()->Push([this, sockfd, client_addr]()
                                                    { this->Service(sockfd, client_addr); });
        }
    }

    void Service(int sockfd, InetAddr C_addr)
    {
        std::string package;
        while (true)
        {
            char inbuffer[SIZE];
            size_t n = ::recv(sockfd, inbuffer, sizeof(inbuffer), 0);
            if (n > 0)
            {
                inbuffer[n] = 0;
                package += inbuffer;

                std::string echo_string = _handler(package); // 处理上层任务
                if (echo_string.empty())
                    continue;

                ::send(sockfd, echo_string.c_str(), echo_string.size(), 0);
            }
            else if (n == 0)
            {
                LOG(INFO, "client %s qiut!\n", C_addr.AddrStr().c_str());
                break;
            }
            else
            {
                LOG(ERROR, "read error: %s\n", C_addr.AddrStr().c_str());
                break;
            }
        }
        ::close(sockfd);
    }

private:
    int _listen_sockfd;
    int _port;

    // 接收上层任务
    handler_t _handler;
};